package com.jiangxjsy.authorization.support;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jiangxjsy.authorization.client.AdminUserClient;
import com.jiangxjsy.core.common.LoginType;
import com.jiangxjsy.core.common.UserStatus;
import com.jiangxjsy.core.constant.AdminCacheKey;
import com.jiangxjsy.core.constant.BasicConstant;
import com.jiangxjsy.core.constant.UserStatusConstant;
import com.jiangxjsy.core.exception.AuthorizationException;
import com.jiangxjsy.core.exception.BasicException;
import com.jiangxjsy.core.exception.UserNotFoundException;
import com.jiangxjsy.core.redis.Redis;
import com.jiangxjsy.core.security.PasswordEncoder;
import com.jiangxjsy.core.security.kit.EncodedCredential;
import com.jiangxjsy.core.wrap.Result;
import com.jiangxjsy.core.wrap.ResultBuilder;
import com.jiangxjsy.core.wrap.ResultCode;
import com.jiangxjsy.core.wrap.ResultHelper;
import com.jiangxjsy.model.authorization.bo.AdminUserStatusBO;
import com.jiangxjsy.model.authorization.entity.AuthorizationUser;
import com.jiangxjsy.model.core.entity.UserExtra;
import com.jiangxjsy.model.core.entity.UserInfo;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * wukong-crm-master
 * 蒋胜宇
 * 2025/6/4 21:39
 */

@Component
public class LoginSupport {
    @Resource
    private AdminUserClient adminUserClient;

    @Resource
    private Redis redis;

    @Resource
    private PasswordEncoder passwordEncoder;

    /**
     * 多次登录失败处理
     *
     * @param user 用户
     * @return Result
     */
    public Result<?> multipleLoginFailed(AuthorizationUser user) {
        LoginType loginType = LoginType.valueOf(user.getLoginType());
        if (loginType.equals(LoginType.PASSWORD_LOGIN) || loginType.equals(LoginType.VERIFICATION_CODE_LOGIN)) {
            String key = AdminCacheKey.PASSWORD_ERROR_CACHE_KEY + user.getUsername().trim();
            Integer errorNum = redis.get(Integer.class, key);

            if (errorNum != null && errorNum > BasicConstant.MAX_LOGIN_ERROR_COUNT) {
                int second = Optional.ofNullable(redis.ttl(key)).orElse(0L).intValue();
                if (second > 0) {
                    String errorTimeDesc = getErrorTimeDesc(second);
                    return ResultBuilder.error(ResultCode.AuthorizationResultCode.AUTHORIZATION_LOGIN_PASSWORD_TO_MANY_ERROR,
                            String.format(ResultCode.AuthorizationResultCode.AUTHORIZATION_LOGIN_PASSWORD_TO_MANY_ERROR.getMsg(), errorTimeDesc));
                }
            }
        }
        return ResultBuilder.ok();
    }

    /**
     * 获取错误次数描述
     *
     * @param second 秒
     * @return 错误次数描述
     */
    private String getErrorTimeDesc(Integer second) {
        String errorTimeDesc;
        if (Arrays.asList(300, 240, 180, 120, 60).contains(second)) {
            errorTimeDesc = second / 60 + "minutes";
        } else if (second < 60) {
            errorTimeDesc = second + "seconds";
        } else {
            errorTimeDesc = second / 60 + "minutes" + second % 60 + "seconds";
        }
        return errorTimeDesc;
    }

    /**
     * 根据用户名获取用户信息
     *
     * @param username 用户名
     * @return UserInfo
     */
    public UserInfo loadUserByUsername(String username) {
        Result<?> result = adminUserClient.findByUsername(username);
        if (ResultHelper.hasSuccess(result)) {
            JSONArray array = JSONArray.parseArray(JSONObject.toJSONString(result.getData()));
            if (array.isEmpty()) {
                throw new UserNotFoundException(ResultCode.AuthorizationResultCode.AUTHORIZATION_USER_DOES_NOT_EXIST);
            }
            if (array.size() > 1) {
                throw new BasicException(ResultCode.SystemResultCode.SYSTEM_ERROR);
            }
            return array.getObject(0, UserInfo.class);
        }
        throw new BasicException(ResultCode.SystemResultCode.SYSTEM_ERROR);
    }

    /**
     * 验证用户身份
     *
     * @param user 登录用户
     * @param userInfo 用户信息
     * @return Result
     */
    public Result<?> validateUserIdentity(AuthorizationUser user, UserInfo userInfo) {
        EncodedCredential encodedCredential = new EncodedCredential(userInfo.getPassword(), userInfo.getSalt());
        if (passwordEncoder.matches(user.getPassword(), encodedCredential)) {
            return ResultBuilder.ok();
        }
        throw new UserNotFoundException(ResultCode.AuthorizationResultCode.AUTHORIZATION_LOGIN_NO_USER);
    }

    /**
     * 验证用户状态
     *
     * @param user 登录用户
     * @param userInfo 用户信息
     * @return Result
     */
    public Result<?> validateUserStatus(@SuppressWarnings("unused") AuthorizationUser user, UserInfo userInfo) {
        if (UserStatus.valueOf(userInfo.getStatus()) == UserStatus.DISABLE) {
            throw new AuthorizationException(ResultCode.AuthorizationResultCode.AUTHORIZATION_USER_DISABLE_ERROR);
        }
        if (UserStatus.valueOf(userInfo.getStatus()) == UserStatus.NOT_ACTIVE) {
            Result<?> ignore = adminUserClient.activateUser(AdminUserStatusBO.builder()
                    .status(UserStatusConstant.NORMAL)
                    .ids(Collections.singletonList(userInfo.getUserId()))
                    .build()
            );
        }
        return ResultBuilder.ok();
    }

    /**
     * 获取用户 token
     *
     * @param type 登录类型
     * @param userId 用户 id
     * @return userToken
     */
    public String getUserToken(Integer type, Long userId){
        return BasicConstant.USER_TOKEN + ":" + type + ":" + userId.toString();
    }

    /**
     * 用户退出
     *
     * @param userId 用户 id
     * @param type 登录类型
     * @param extra 额外信息，1 代表被挤掉提示
     */
    public void userExit(Long userId, Integer type, Integer extra) {
        String token = null, key = getUserToken(type, userId);
        if (redis.hasKey(key)) {
            token = redis.get(String.class, key);
            redis.delete(key);
        }

        if (Objects.equals(1, extra) && token != null) {
            Long time = redis.ttl(token);
            if (time > 1L) {
                redis.setEx(token, new UserExtra(1, DateUtil.formatDateTime(new Date())), time.intValue(), TimeUnit.MINUTES);
            }
        } else {
            if (token != null) {
                redis.delete(token);
            }
        }
    }
}
